package com.knife.easyexcel.read.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.knife.easyexcel.read.chain.BatchImportValidatorHelperChain;
import com.knife.easyexcel.read.data.ValidateElementResult;
import com.knife.easyexcel.read.exception.ExcelValidateException;
import com.knife.easyexcel.read.message.DefaultImportListenerErrorMsgBuilder;
import com.knife.easyexcel.read.message.DefaultImportValidatorErrorMsgBuilder;
import com.knife.easyexcel.read.message.ImportListenerErrorMsgBuilder;
import com.knife.easyexcel.read.message.ImportValidatorErrorMsgBuilder;
import com.knife.easyexcel.read.validator.ImportValidator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

/**
 * 读取excel行数据并进行校验
 *
 * @author Win10
 * @created 2024年12月19日 星期四 10:01
 */
@Slf4j
public class UploadExcelValidateListener<T> extends AnalysisEventListener<T> {
    /**
     * 存放excel读取的数据
     */
    private volatile List<T> tempList;
    /**
     * 校验器
     */
    private final List<ImportValidator<T>> importValidatorList;
    /**
     * 用于标记导入数据的唯一性键，常用于返回报错信息中的数据前缀
     *
     * 例如：特种鱼膨化配合饲料8502：1.品种分组不能为空  中的 特种鱼膨化配合饲料8502
     */
    private Function<T, String> elementKeyFunc;
    /**
     * 最终校验结果
     */
    private Boolean validateResult;
    /**
     * 校验结果
     */
    private List<ValidateElementResult<T>> validateElementResultList;
    /**
     * 最终异常信息构造器
     */
    private ImportListenerErrorMsgBuilder<T> importListenerErrorMsgBuilder;
    /**
     * 单条数据校验信息构造器
     */
    private ImportValidatorErrorMsgBuilder<T> importValidatorErrorMsgBuilder;


    public UploadExcelValidateListener(Function<T, String> elementKeyFunc) {
        this.tempList = new ArrayList<>();
        this.importValidatorList = new ArrayList<>();
        this.elementKeyFunc = elementKeyFunc;
        this.validateElementResultList = new ArrayList<>();
        this.validateResult = false;
        this.importListenerErrorMsgBuilder = new DefaultImportListenerErrorMsgBuilder<>();
        this.importValidatorErrorMsgBuilder = new DefaultImportValidatorErrorMsgBuilder<>();
    }

    /**
     * 追加校验器
     *
     * @param cuxImportValidator 自定义校验器
     * @return 校验监听器
     */
    public UploadExcelValidateListener<T> addValidator(ImportValidator<T> cuxImportValidator) {
        importValidatorList.add(cuxImportValidator);
        return this;
    }

    /**
     * 最终校验异常的时候，构造异常信息
     *
     * @param importListenerErrorMsgBuilder 异常信息构造器
     * @return 当前对象
     */
    public UploadExcelValidateListener<T> replaceListenerErrorMsgBuilder(ImportListenerErrorMsgBuilder<T> importListenerErrorMsgBuilder) {
        this.importListenerErrorMsgBuilder = importListenerErrorMsgBuilder;
        return this;
    }

    /**
     * 单条数据校验信息异常的时候，构造异常信息
     *
     * @param importValidatorErrorMsgBuilder 异常信息构造器
     * @return 当前对象
     */
    public UploadExcelValidateListener<T> replaceValidatorErrorMsgBuilder(ImportValidatorErrorMsgBuilder<T> importValidatorErrorMsgBuilder) {
        this.importValidatorErrorMsgBuilder = importValidatorErrorMsgBuilder;
        return this;
    }

    /**
     * 获取校验结果数据
     *
     * @return 结果数据
     */
    public List<ValidateElementResult<T>> getValidateElementResultList() {
        return validateElementResultList;
    }

    @Override
    public void invoke(T data, AnalysisContext context) {
        tempList.add(data);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (CollectionUtils.isEmpty(tempList)) {
            throw new RuntimeException("excel中未找到数据，请先维护数据再进行导入");
        }
        // 校验
        BatchImportValidatorHelperChain.Builder<T> builder = BatchImportValidatorHelperChain.Builder.create();
        BatchImportValidatorHelperChain<T> helperChain = builder.data(tempList)
                .validator(importValidatorList)
                .errorMsgBuilder(this.importValidatorErrorMsgBuilder)
                .build();
        this.validateResult = helperChain.doValidate(elementKeyFunc, validateElementResultList);
        // 校验失败抛出异常
        if (!validateResult) {
            throw new ExcelValidateException(importListenerErrorMsgBuilder.build(validateElementResultList));
        }
    }

}
